package org.amos.gen.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.amos.core.basic.utils.AmosUtils;
import org.amos.core.basic.utils.FileUtils;
import org.amos.gen.base.*;
import org.amos.gen.common.GeneratorParam;
import org.amos.gen.dto.*;
import org.amos.gen.entity.*;
import org.amos.gen.service.*;
import org.amos.gen.utils.FormatUtil;
import org.amos.gen.utils.VelocityUtil;
import org.amos.gen.vo.GenColumnVo;
import org.amos.gen.vo.GenTableVo;
import org.amos.gen.vo.GenValidVo;
import org.apache.velocity.VelocityContext;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Service
@RequiredArgsConstructor
public class GenServiceImpl implements GenService {

    private final GenTableService tableInfoService;
    private final GenColumnInfoService columnInfoService;
    private final GenDatasourceService genDatasourceService;
    private final GenTemplateService genTemplateService;
    private final GenValidInfoService genValidInfoService;

    @Override
    public IPage<GenTableInfo> getGenTablePage(Page<GenTableInfo> page, QueryWrapper queryWrapper) {
        return tableInfoService.page(page, queryWrapper);
    }

    @Override
    public List<GenColumnVo> getGenColDefaultList(String tableName) {
        return null;
    }

    @Override
    public List<GenColumnVo> getGenColumnList(String tableName) {
        QueryWrapper<GenColumnInfo> qw = new QueryWrapper<>();
        qw.eq("table_name", tableName);
        List<GenColumnInfo> columnInfos = columnInfoService.list(qw);
        return AmosUtils.listCopy(columnInfos, GenColumnVo.class);
    }

    @Override
    public List<GenValidVo> getGenCoValidDefaultlList(String tableName) {
        List<GenValidVo> vos = new ArrayList<>();

        return vos;
    }

    @Override
    public List<GenValidVo> getGenColumnValidList(String tableName) {
        QueryWrapper<GenValidInfo> qw = new QueryWrapper<>();
        qw.eq("table_name", tableName);
        List<GenValidInfo> validInfos = genValidInfoService.list(qw);
        return AmosUtils.listCopy(validInfos, GenValidVo.class);
    }

    @Override
    public GenTableVo getGenTableInfo(String tableName) {
        QueryWrapper<GenTableInfo> qw = new QueryWrapper<>();
        qw.eq("table_name", tableName);
        GenTableInfo info = tableInfoService.getOne(qw);
        return ObjectUtil.isEmpty(info) ? null : AmosUtils.copy(info, GenTableVo.class);
    }

    @Override
    public GenTableVo getGenTableDefaultInfo(String tableName) {
        GenTableVo vo = new GenTableVo();
        vo.setTableName(tableName);
        vo.setAuthor("刘伯韬");
        vo.setModuleName("AmosGen");
        vo.setPackageName("org.amos.gen.test");
        return vo;
    }

    @Override
    public Boolean saveGenTableInfo(GenTableDTO dto) {
        return tableInfoService.save(AmosUtils.copy(dto, GenTableInfo.class));
    }

    @Override
    public Boolean updateGenTableInfo(GenTableDTO dto) {
        UpdateWrapper<GenTableInfo> uw = new UpdateWrapper<>(AmosUtils.copy(dto, GenTableInfo.class));
        return tableInfoService.update(uw);
    }

    @Override
    public Boolean saveGenColumnInfo(GenColDTO dto) {
        return columnInfoService.save(AmosUtils.copy(dto, GenColumnInfo.class));
    }

    @Override
    public Boolean updateGenColumnInfo(GenColDTO dto) {
        UpdateWrapper<GenColumnInfo> uw = new UpdateWrapper<>(AmosUtils.copy(dto, GenColumnInfo.class));
        return columnInfoService.update(uw);
    }

    @Override
    public List<CodeFile> genCode(GeneratorParam generatorParam, GeneratorConfig generatorConfig) {
        List<SQLContext> contextList = this.buildSQLContextList(generatorParam, generatorConfig);
        List<CodeFile> codeFileList = new ArrayList<>();

        for (SQLContext sqlContext : contextList) {
            setPackageName(sqlContext, generatorParam.getPackageName());
            setDelPrefix(sqlContext, generatorParam.getDelPrefix());
            setAuthor(sqlContext, generatorParam.getAuthor());
            for (long tcId : generatorParam.getTemplateConfigIdList()) {
                GenTemplate template = genTemplateService.getById(tcId);
                String folder = template.getFolder();
                if (StrUtil.isBlank(folder)) {
                    folder = template.getName();
                } else {
                    //文件目录可以使用变量
                    folder = doGenerator(sqlContext, folder);
                }

                setFolder(sqlContext, folder);

                //获取文件名
                String fileName = doGenerator(sqlContext, template.getFileName());
                String content = doGenerator(sqlContext, template.getContent());
                content = this.formatCode(fileName, content);
                CodeFile codeFile = new CodeFile();
                codeFile.setFolder(folder);
                codeFile.setFileName(fileName);
                codeFile.setContent(content);
                codeFileList.add(codeFile);
            }
        }
        return codeFileList;
    }

    @Override
    public void downloadCode(Long id, HttpServletResponse response) {
        //生成Java代码
        GenPreInfo preInfo = this.genCommon(id);
//        String filePath = genUtil.genCodeDownload(preInfo.getGenInfo(), preInfo.getGenConfig());
        String filePath = null;
        //生成HTML代码

        File file = new File(filePath);
        String zipPath = file.getPath() + ".zip";
        ZipUtil.zip(file.getPath(), zipPath);
        FileUtils.downloadFile(response, new File(zipPath), Boolean.TRUE);
    }

    @Override
    public Object previewCode(Long id) {
        //代码预览
        GenPreInfo preInfo = this.genCommon(id);
        List<CodeFile> codeFiles = new ArrayList<>();
        return codeFiles;
    }

    @Override
    public GenPreInfo genCommon(Long id) {
        GenTableInfo info = tableInfoService.getById(id);
        List<String> tableNames = new ArrayList<>();
        List<Long> templates = new ArrayList<>();

        Long datasourceId = info.getDataSourceId();
        Long groupId = info.getGroupId();
        GenInfo genInfo = AmosUtils.copy(info, GenInfo.class);

        QueryWrapper<GenTemplate> qw = new QueryWrapper<>();
        qw.eq("group_id", groupId);
        List<GenTemplate> genTemplateList = genTemplateService.list(qw);
        genTemplateList.forEach(t -> {
            templates.add(t.getId());
        });
        tableNames.add(info.getTableName());

        genInfo.setTableNames(tableNames);
        genInfo.setTemplateIds(templates);
        genInfo.setDatasourceId(datasourceId);
        GenDatasource datasourceConfig = genDatasourceService.getById(datasourceId);
        GeneratorConfig genConfig = GeneratorConfig.build(datasourceConfig);

        GenPreInfo preInfo = new GenPreInfo();
        preInfo.setGenInfo(genInfo);
        preInfo.setGenConfig(genConfig);

        return preInfo;
    }

    @Override
    public Boolean saveGenSetting(GenSettingDTO dto) {
        List<GenColumnInfoDTO> columns = dto.getColumns();
        List<GenValidDTO> valids = dto.getValids();
        GenTableDTO table = dto.getTable();

        tableInfoService.saveOrUpdate(AmosUtils.copy(table, GenTableInfo.class));
        columnInfoService.saveOrUpdateBatch(AmosUtils.listCopy(columns, GenColumnInfo.class));
        genValidInfoService.saveOrUpdateBatch(AmosUtils.listCopy(valids, GenValidInfo.class));

        return Boolean.TRUE;
    }

    /**
     * 格式化代码
     *
     * @param fileName
     * @param content
     * @return
     */
    private String formatCode(String fileName, String content) {
        if (fileName.endsWith(".xml")) {
            return FormatUtil.formatXml(content);
        }
        return content;
    }

    /**
     * 返回SQL上下文列表
     *
     * @param generatorParam  参数
     * @param generatorConfig 配置
     * @return 返回SQL上下文
     */
    private List<SQLContext> buildSQLContextList(GeneratorParam generatorParam, GeneratorConfig generatorConfig) {

        List<String> tableNames = generatorParam.getTableNames();
        List<SQLContext> contextList = new ArrayList<>();
        SQLService service = SQLServiceFactory.build(generatorConfig);

        TableSelector tableSelector = service.getTableSelector(generatorConfig);
        tableSelector.setSchTableNames(tableNames);

        List<TableDefinition> tableDefinitions = tableSelector.getTableDefinitions();

        for (TableDefinition tableDefinition : tableDefinitions) {
            SQLContext sqlContext = new SQLContext(tableDefinition);
            sqlContext.setDbName(generatorConfig.getDbName());
            contextList.add(sqlContext);
        }

        return contextList;
    }

    private void setPackageName(SQLContext sqlContext, String packageName) {
        if (StringUtils.hasText(packageName)) {
            sqlContext.setPackageName(packageName);
        }
    }

    private void setFolder(SQLContext sqlContext, String folder) {
        if (StringUtils.hasText(folder)) {
            sqlContext.setPackageSubPath(folder);
        }
    }

    private void setDelPrefix(SQLContext sqlContext, String delPrefix) {
        if (StringUtils.hasText(delPrefix)) {
            sqlContext.setDelPrefix(delPrefix);
        }
    }

    private void setAuthor(SQLContext sqlContext, String author) {
        if (StringUtils.hasText(author)) {
            sqlContext.setAuthor(author);
        }
    }

    private String doGenerator(SQLContext sqlContext, String template) {
        if (template == null) {
            return "";
        }
        VelocityContext context = new VelocityContext();
        Object pkColumn = sqlContext.getTableDefinition().getPkColumn();
        if (pkColumn == null) {
            pkColumn = Collections.emptyMap();
        }
        context.put("context", sqlContext);
        context.put("table", sqlContext.getTableDefinition());
        context.put("pk", pkColumn);
        context.put("columns", sqlContext.getTableDefinition().getColumnDefinitions());
        context.put("csharpColumns", sqlContext.getTableDefinition().getCsharpColumnDefinitions());

        return VelocityUtil.generate(context, template);
    }
}
